#include "NonPrimitives.h"
#include <stdio.h>
#include <iostream>
using namespace std;

float no_curve_points = 4; // points to approximate a curve
int segments = 3; // number of bezier curves
float angle_inc = M_PI / 16; // the rotation step size
int circle_size = 2 * M_PI / angle_inc; // the number of vertices in a rotation
int no_vertices = no_curve_points * segments * 3; // the number of vertices in the bezier curve

BezierObject::BezierObject() {
	// points picked from java applet
	float control_points[45] = {
			242, 26, 0, 0,
			241, 91, 0, 0,
			276, 132, 0, 0,
			326, 156, 0, 0,
			359, 172, 0, 0,
			378, 231, 0, 0,
			328, 269, 0, 0,
			291, 295, 0, 0,
			273, 326, 0, 0,
			318, 449, 0, 0 };

	// fix points
	for (int i = 0; i < 45; i+=4) {
		control_points[i] -= 242;
	}

	// instantiate
	T = new Vector4();
	C = new Matrix4();
	G = new Matrix4();
	B = new Matrix4();
	n = new Vector4();
	n->set(0, 200, 0, 1); // center
	rotated_vertices = new float[no_vertices * circle_size];
	normals = new float[no_vertices * circle_size];
	texture_coords = new float[no_vertices * circle_size];
	float temp2[16] = { -1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0 };
	B->set(temp2);
	// calculate the rotated bezier surface
	getBezierSurface(segments, control_points, no_curve_points, angle_inc);
}

Vector4 BezierObject::x(float t) {
	T->set(t * t * t, t * t, t, 1);
	return C->vector4Mul(*T);
}

void BezierObject::getBezierSurface(int segments, float control_points[],
		int no_curve_points, float angle_inc) {
	// iterate over circle from single vertex rotation
	int count = 0;
	for (float v = 0; v < 2 * M_PI; v += angle_inc) {
		//cout << "angle_inc: " << v << endl;
		Matrix4 *rot = new Matrix4();
		rot->rotationY(v * 180 / M_PI);
		// iterate over each bezier piece
		for (int seg = 0; seg < segments; seg++) {
			// set C matrix
			float temp[16];
			for (int k = 0; k < 16; k++)
				temp[k] = control_points[k + 3 * 4 * seg];
			G->set(temp);
			C->set(G->get());
			C->multiply(*B);
			// iterate over the points approximating one curve
			for (int k = 0; k < no_curve_points; k++) {
				float step = 1.0 / no_curve_points;
				Vector4 vec = x(step * k);
				vec = rot->vector4Mul(vec);
				int index = count * no_vertices + seg * no_curve_points * 3
						+ k * 3;
				// vertices
				rotated_vertices[index] = vec.get()[0];
				rotated_vertices[index + 1] = vec.get()[1];
				rotated_vertices[index + 2] = vec.get()[2];
				// normals
				vec.subtract(*n);
				vec.normalize();
				//vec.printVector();
				normals[index] = vec.get()[0];
				normals[index + 1] = vec.get()[1];
				normals[index + 2] = vec.get()[2];
			}
		}
		count++;
	}
	/* print rotated_vertices
	for (int i = 0; i < no_vertices * circle_size; i += 3)
		cout << "(" << rotated_vertices[i] << "," << rotated_vertices[i + 1]
				<< "," << rotated_vertices[i + 2] << ")" << endl;
	*/
}

void BezierObject::render() {
	t->concreteTexture();
	glBegin(GL_QUADS);
	int lower_left, lower_right, upper_right, upper_left;
	glColor3f(1, 1, 1);
	for (int c = 0; c < circle_size; c++) {
		for (int s = 0; s < segments; s++) {
			for (int p = 0; p < no_curve_points; p++) {
				//if(c == 0) glColor3f(1, 1, 1);
				//else if(c == 1) glColor3f(0, 0, 1);
				//else glColor3f(1, 0, 0);
				int lower_left = c * no_vertices + s * no_curve_points * 3 + p * 3,
					lower_right = (c+1) * no_vertices + s * no_curve_points * 3 + p * 3,
					upper_right = (c+1) * no_vertices + s * no_curve_points * 3 + (p+1) * 3,
					upper_left = c * no_vertices + s * no_curve_points * 3 + (p+1) * 3;
				if(s == 1 && p == 2) glColor3f(0, 0, 1);
				glTexCoord2f(0,1);
				glVertex3f(rotated_vertices[lower_left],
						   rotated_vertices[lower_left + 1],
						   rotated_vertices[lower_left + 2]);
				glTexCoord2f(1,1);
				glVertex3f(rotated_vertices[lower_right],
						   rotated_vertices[lower_right + 1],
						   rotated_vertices[lower_right + 2]);
				glTexCoord2f(1,0);
				glVertex3f(rotated_vertices[upper_right],
						   rotated_vertices[upper_right + 1],
						   rotated_vertices[upper_right + 2]);
				glTexCoord2f(0,0);
				glVertex3f(rotated_vertices[upper_left],
						   rotated_vertices[upper_left + 1],
						   rotated_vertices[upper_left + 2]);
				glColor3f(1, 1, 1);
				if(c == circle_size-1){
					//glColor3f(0, 1, 0);
					int lower_left = c * no_vertices + s * no_curve_points * 3 + p * 3,
						lower_right = 0 * no_vertices + s * no_curve_points * 3 + p * 3,
						upper_right = 0 * no_vertices + s * no_curve_points * 3 + (p+1) * 3,
						upper_left = c * no_vertices + s * no_curve_points * 3 + (p+1) * 3;
					if(s == 1 && p == 2) glColor3f(0, 0, 1);
					glTexCoord2f(0,1);
					glVertex3f(rotated_vertices[lower_left],
							   rotated_vertices[lower_left + 1],
							   rotated_vertices[lower_left + 2]);
					glTexCoord2f(1,1);
					glVertex3f(rotated_vertices[lower_right],
							   rotated_vertices[lower_right + 1],
							   rotated_vertices[lower_right + 2]);
					glTexCoord2f(1,0);
					glVertex3f(rotated_vertices[upper_right],
							   rotated_vertices[upper_right + 1],
							   rotated_vertices[upper_right + 2]);
					glTexCoord2f(0,0);
					glVertex3f(rotated_vertices[upper_left],
							   rotated_vertices[upper_left + 1],
							   rotated_vertices[upper_left + 2]);
					glColor3f(1, 1, 1);
				}
			}
		}
	}
	glEnd();
}

void BezierObject::boundingSphere(Matrix4 *c, Texture *tex) {
	t = tex;
}
